package jbitarrays;

import javax.annotation.Nonnegative;
import javax.annotation.Nonnull;
import javax.annotation.concurrent.NotThreadSafe;

import jbitarrays.api.CrawlerReader;
import jbitarrays.api.CrawlerWriter;

/**
 * @see JBitArrays#skipReader(CrawlerReader, int, int)
 */
@NotThreadSafe final class PeriodicCrawlerReader implements CrawlerReader {
	private final CrawlerReader decorated;
	private final int readLength;
	private final int skipLength;
	private int bitsLeftToRead;

	public PeriodicCrawlerReader(@Nonnull CrawlerReader decorated, @Nonnegative int readLength, @Nonnegative int skipLength) {
		this(decorated, readLength, skipLength, readLength);
	}

	PeriodicCrawlerReader(@Nonnull CrawlerReader decorated, @Nonnegative int readLength, @Nonnegative int skipLength, @Nonnegative int bitsLeftToRead) {
		if (decorated == null || skipLength < 0 || readLength <= 0) {
			throw new IllegalArgumentException("decorated != null && skipLength >= 0 && readLength > 0");
		}
		this.decorated = decorated;
		this.readLength = readLength;
		this.skipLength = skipLength;
		this.bitsLeftToRead = bitsLeftToRead;
	}

	public boolean getNext() {
		skipIfRequired();
		--bitsLeftToRead;
		return decorated.getNext();
	}

	private void skipIfRequired() {
		if (bitsLeftToRead == 0) {
			int bitsToSkip = skipLength;
			while (bitsToSkip > 0) {
				int toRead = Math.min(bitsToSkip, Integer.SIZE);
				decorated.getNextN(toRead);
				bitsToSkip -= toRead;
			}
			bitsLeftToRead = readLength;
		}
	}

	public CrawlerWriter toWriter() {
		return new PeriodicCrawlerWriter(decorated.toWriter(), readLength, skipLength, bitsLeftToRead);
	}

	@Override public CrawlerReader clone() {
		return new PeriodicCrawlerReader(decorated.clone(), readLength, skipLength, bitsLeftToRead);
	}

	public int getNextN(int howMany) {
		Utils.checkHowMany(howMany);
		int ret = 0;
		while (howMany > 0) {
			skipIfRequired();
			int toRead = Math.min(howMany, bitsLeftToRead);
			howMany -= toRead;
			bitsLeftToRead -= toRead;
			ret |= decorated.getNextN(toRead) << howMany;
		}
		return ret;
	}
}
